package com.pzl.util;

import android.util.Base64;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.Key;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.util.Map;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.IvParameterSpec;

/**
 * 加密算法工具
 */
public class AlgorithmUtil {
    /**
     * ALGORITHM 算法 <br>
     * 可替换为以下任意一种算法，同时key值的size相应改变。
     * <p/>
     * <pre>
     * DES                  key size must be equal to 56
     * DESede(TripleDES)    key size must be equal to 112 or 168
     * AES                  key size must be equal to 128, 192 or 256,but 192 and 256 bits may not be available
     * Blowfish             key size must be multiple of 8, and can only range from 32 to 448 (inclusive)
     * RC2                  key size must be between 40 and 1024 bits
     * RC4(ARCFOUR)         key size must be between 40 and 1024 bits
     * </pre>
     * <p/>
     * 在Key toKey(byte[] key)方法中使用下述代码
     * <p>SecretKey secretKey = new SecretKeySpec(key, ALGORITHM);</p> 替换
     * <p>
     * DESKeySpec dks = new DESKeySpec(key);<br>
     * SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM);<br>
     * SecretKey secretKey = keyFactory.generateSecret(dks);<br>
     * </p>
     */
    public static final String ALGORITHM = "DES";
    /**
     * 父级节点 key
     */
    public static String parentKey = "E8450B38C7DD622CEB5A369F2D7F10A4AC80F53A";

    private static byte[] iv = {1, 2, 3, 4, 5, 6, 7, 8};

    /**
     * 加密算法
     *
     * @param source 需要加密的对象
     * @param key    加密使用的Key
     * @return 加密后的数据
     */
    public static String encrypt(String source, String key) {
        try {
            byte[] bs = source.getBytes("UTF-8");
            System.out.println(parseByte2HexStr(bs));
            bs = parseByte2HexStr(bs).getBytes();
            IvParameterSpec zeroIv = new IvParameterSpec(iv);
            Key k = toKey(parentKey + "_" + key);
            Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, k, zeroIv);
            byte[] d = cipher.doFinal(bs);
            return encodeBASE64(d);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 解密算法
     *
     * @param source 需要解密的对象
     * @param key    加密使用的Key
     * @return 解密后的数据
     */
    public static String decode(String source, String key) {
        try {
            byte[] result = decodeBASE64(source);
            IvParameterSpec zeroIv = new IvParameterSpec(iv);
            Key k = toKey(parentKey + "_" + key);
            Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, k, zeroIv);
            byte[] d = cipher.doFinal(result);
            d = parseHexStr2Byte(new String(d));
            return new String(d, "UTF-8");
        } catch (Exception e) {
            return null;
        }
    }

    public static String decodeDES(String source, String key) {
        try {
            IvParameterSpec zeroIv = new IvParameterSpec(iv);
            Key k = toKey(md5crypt(key));
            Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, k, zeroIv);
            byte[] d = cipher.doFinal(source.getBytes());
            return encodeBASE64(d);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return key;
    }

    /**
     * One-stop md5 string encrypting.
     */
    public static String md5crypt(String input) {
        return AlgorithmUtil.md5crypt(input.getBytes());
    }

    public static String md5crypt(byte[] input) {
        try {
            char[] hexChar = {'0', '1', '2', '3', '4', '5', '6', '7',
                    '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            md5.update(input);
            input = md5.digest();
            StringBuilder sb = new StringBuilder(input.length * 2);
            for (int i = 0; i < input.length; i++) {
                sb.append(hexChar[(input[i] & 0xf0) >>> 4]);
                sb.append(hexChar[input[i] & 0x0f]);
            }
            return sb.toString();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 转换密钥<br>
     *
     * @param key
     * @return
     * @throws Exception
     */
    private static Key toKey(String key) throws Exception {
        DESKeySpec dks = new DESKeySpec((md5crypt(key)).getBytes());
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM);
        SecretKey secretKey = keyFactory.generateSecret(dks);
        return secretKey;
    }

    private static String hexStr = "0123456789ABCDEF";

    /**
     * 将二进制转换成16进制
     *
     * @param buf
     * @return
     */
    public static String parseByte2HexStr(byte buf[]) {
        String result = "";
        String hex = "";
        for (int i = 0; i < buf.length; i++) {
            // 字节高4位
            hex = String.valueOf(hexStr.charAt((buf[i] & 0xF0) >> 4));
            // 字节低4位
            hex += String.valueOf(hexStr.charAt(buf[i] & 0x0F));
            result += hex;
        }
        return result;
    }

    /**
     * 将16进制转换为二进制
     *
     * @param hexStr
     * @return
     */
    public static byte[] parseHexStr2Byte(String hexStr) {
        if (hexStr.length() < 1)
            return null;
        byte[] result = new byte[hexStr.length() / 2];
        for (int i = 0; i < hexStr.length() / 2; i++) {
            int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16);
            int low = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2), 16);
            result[i] = (byte) (high * 16 + low);
        }
        return result;
    }

    /**
     * BASE64解密
     *
     * @param key
     * @return
     * @throws IOException
     * @throws Exception
     */
    @SuppressWarnings("static-access")
    public static byte[] decodeBASE64(String key) throws IOException {
        return Base64.decode(key, Base64.DEFAULT);
    }

    /**
     * BASE64加密
     *
     * @param key
     * @return
     * @throws Exception
     */
    @SuppressWarnings("static-access")
    public static String encodeBASE64(byte[] key) {
        return Base64.encodeToString(key, Base64.DEFAULT);
    }

    /**
     * Encode a string using algorithm specified in web.xml and return the
     * resulting encrypted password. If exception, the plain credentials string
     * is returned
     *
     * @param password  Password or other credentials to use in authenticating this
     *                  username
     * @param algorithm Algorithm used to do the digest
     * @return encypted password based on the algorithm.
     */
    public static String encodePassword(String password, String algorithm) {
        byte[] unencodedPassword = password.getBytes();
        MessageDigest md = null;
        try {
            // first create an instance, given the provider
            md = MessageDigest.getInstance(algorithm);
        } catch (Exception e) {
            return password;
        }
        md.reset();
        // call the update method one or more times
        // (useful when you don't know the size of your data, eg. stream)
        md.update(unencodedPassword);
        // now calculate the hash
        byte[] encodedPassword = md.digest();
        StringBuffer buf = new StringBuffer();
        for (int i = 0; i < encodedPassword.length; i++) {
            if ((encodedPassword[i] & 0xff) < 0x10) {
                buf.append("0");
            }
            buf.append(Long.toString(encodedPassword[i] & 0xff, 16));
        }
        return buf.toString();
    }

    /**
     * 压缩字符串数据
     *
     * @param source
     * @return
     */
    public static String encodeGZIP(String source) {
        ByteArrayOutputStream out = null;
        GZIPOutputStream stream = null;
        try {
            out = new ByteArrayOutputStream();
            stream = new GZIPOutputStream(out);
            stream.write(source.getBytes());
            stream.finish();
            out.flush();
            return out.toString("ISO-8859-1");
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if (stream != null) {
                    stream.close();
                }
                if (out != null) {
                    out.close();
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * 解压字符串数据
     *
     * @param source
     * @return
     */
    public static String decodeGZIP(String source) {
        ByteArrayInputStream in = null;
        GZIPInputStream stream = null;
        ByteArrayOutputStream out = null;
        try {
            in = new ByteArrayInputStream(source.getBytes("ISO-8859-1"));
            stream = new GZIPInputStream(in);
            out = new ByteArrayOutputStream();
            byte[] bt = new byte[1024];
            int len = -1;
            while ((len = stream.read(bt)) > 0) {
                out.write(bt, 0, len);
            }
            String s = out.toString();
            out.flush();
            return s;
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if (stream != null) {
                    stream.close();
                }
                if (in != null) {
                    in.close();
                }
                if (out != null) {
                    out.close();
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * 二行制转字符串
     *
     * @param b
     * @return
     */
    private static String byte2hex(byte[] b) {
        StringBuilder hs = new StringBuilder();
        String stmp;
        for (int n = 0; b != null && n < b.length; n++) {
            stmp = Integer.toHexString(b[n] & 0XFF);
            if (stmp.length() == 1)
                hs.append('0');
            hs.append(stmp);
        }
        return hs.toString().toUpperCase();
    }


    public static final String REWARD_SECURITY_KEY = "19328_etanod_cso_4_yek_sed_a_si_sihT";


    public static String encryptStr(String data, String key) {
        return byte2hex(encrypt(data.getBytes(), key.getBytes()));
    }


    /**
     * 加密函数
     *
     * @param data 加密数据
     * @param key  密钥
     * @return 返回加密后的数据
     */
    public static byte[] encrypt(byte[] data, byte[] key) {
        try {
            SecureRandom sr = new SecureRandom();
            DESKeySpec dks = new DESKeySpec(key);
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
            SecretKey secretKey = keyFactory.generateSecret(dks);
            Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, secretKey, sr);
            return cipher.doFinal(data);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static String decrypt(String data, String key) {
        return decrypt(data.getBytes(), key.getBytes());
    }

    /**
     * 解密函数
     *
     * @param data 解密数据
     * @param key  密钥
     * @return 返回解密后的数据
     */
    public static String decrypt(byte[] data, byte[] key) {
        try {
            SecureRandom sr = new SecureRandom();
            DESKeySpec dks = new DESKeySpec(key);
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
            SecretKey secretKey = keyFactory.generateSecret(dks);
            Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, secretKey, sr);
            return new String(cipher.doFinal(data));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static String sign(Map<String, String> pairs) {
        String sign = "";
        for (Map.Entry<String, String> pair : pairs.entrySet()) {
            //if (TextUtils.isEmpty(pair.getValue())) continue;
            sign += "&" + pair.getKey() + "=" + pair.getValue();
        }
        sign = sign.substring(1, sign.length() - 1);
        return encryptStr(sign, REWARD_SECURITY_KEY);
    }


}